import { Processor, Process } from '@nestjs/bull';
import { Logger } from '@nestjs/common';
import { Job } from 'bull';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, DataSource } from 'typeorm';
import {
  NovelParseTask,
  TaskStatus,
} from '../entities/novel-parse-task.entity';
import { Novel } from '../entities/novel.entity';
import { NovelChapter } from '../entities/novel-chapter.entity';
import { QidianCrawlerV5 } from '../services/qidian-crawler-v5.service';
import { CategoryMappingService } from '../services/category-mapping.service';
import { PageUtils } from '../utils/page-utils';

export interface NovelParseJobData {
  taskId: string;
  novelId: number;
  originalUrl: string;
  novelTitle: string;
  platform: string;
}

@Processor('novel-parse')
export class NovelParseProcessor {
  private readonly logger = new Logger(NovelParseProcessor.name);

  constructor(
    @InjectRepository(NovelParseTask)
    private readonly taskRepository: Repository<NovelParseTask>,
    @InjectRepository(Novel)
    private readonly novelRepository: Repository<Novel>,
    @InjectRepository(NovelChapter)
    private readonly chapterRepository: Repository<NovelChapter>,
    private readonly categoryMappingService: CategoryMappingService,
    private readonly dataSource: DataSource,
  ) {}

  @Process('chapter-content')
  async handleChapterContent(job: Job<NovelParseJobData>) {
    const { taskId, novelId, originalUrl, novelTitle, platform } = job.data;

    this.logger.log(`开始处理异步章节内容任务: ${taskId}, 小说ID: ${novelId}`);

    try {
      // 幂等性检查：如果任务已完成或正在处理，则跳过
      const existingTask = await this.taskRepository.findOne({
        where: { taskId },
      });

      if (existingTask && existingTask.status === TaskStatus.COMPLETED) {
        this.logger.log(`任务已完成，跳过处理: ${taskId}`);
        return; // 直接返回，不重复处理
      }

      // 检查是否有超时的处理中任务（服务重启后的任务应该被重新处理）
      const now = new Date();
      if (existingTask && existingTask.status === TaskStatus.PROCESSING) {
        const processingTimeout = 5 * 60 * 1000; // 5分钟超时（服务重启场景下缩短超时时间）
        const processingTime = existingTask.startedAt
          ? now.getTime() - existingTask.startedAt.getTime()
          : 0;

        // 检查是否可能是服务重启导致的僵尸任务
        const isLikelyServiceRestart = processingTime > 2 * 60 * 1000; // 超过2分钟认为是重启

        if (processingTime > processingTimeout || isLikelyServiceRestart) {
          this.logger.warn(
            `检测到可能的服务重启或任务超时，重新开始处理: ${taskId}, 已处理时间: ${Math.round(processingTime / 1000)}秒`,
          );

          // 重置任务状态，允许重新处理，但不增加重试次数（因为这不是真正的失败）
          await this.updateTaskStatus(taskId, {
            status: TaskStatus.PENDING,
            errorMessage: isLikelyServiceRestart
              ? '检测到服务重启，重新开始处理'
              : '任务处理超时，自动重试',
            startedAt: null, // 清除开始时间
            // 不增加retryCount，因为这不是失败而是重启
          });
        } else {
          this.logger.log(
            `任务正在正常处理中，跳过重复处理: ${taskId}, 已处理: ${Math.round(processingTime / 1000)}秒`,
          );
          return; // 直接返回，避免并发处理
        }
      }

      // 更新任务状态为处理中
      await this.updateTaskStatus(taskId, {
        status: TaskStatus.PROCESSING,
        startedAt: new Date(),
        progress: 0,
      });

      // 根据平台选择对应的爬虫
      let crawler: QidianCrawlerV5;
      if (platform === 'qidian') {
        crawler = new QidianCrawlerV5(
          this.novelRepository,
          this.chapterRepository,
          this.dataSource.getRepository('MasterCategory'), // masterCategoryRepository
          this.dataSource,
          this.categoryMappingService,
        );
      } else {
        throw new Error(`不支持的平台: ${platform}`);
      }

      // 初始化浏览器
      await crawler.initBrowser();

      try {
        // 获取章节列表（带内容）
        const chapters = await this.extractChaptersWithContent(
          crawler,
          originalUrl,
          taskId,
          novelId,
        );

        // 批量保存章节内容
        await this.saveChapterContents(novelId, chapters, taskId);

        // 更新任务为完成状态
        await this.updateTaskStatus(taskId, {
          status: TaskStatus.COMPLETED,
          progress: 100,
          completedAt: new Date(),
          processedChapters: chapters.length,
          successChapters: chapters.filter((c) => c.content && c.content.trim())
            .length,
        });

        this.logger.log(
          `章节内容处理完成: ${taskId}, 成功处理 ${chapters.length} 章`,
        );
      } finally {
        await crawler.closeBrowser();
      }
    } catch (error) {
      this.logger.error(`章节内容处理失败: ${taskId}`, error);

      // 更新任务为失败状态
      await this.updateTaskStatus(taskId, {
        status: TaskStatus.FAILED,
        errorMessage: error.message,
        completedAt: new Date(),
      });

      throw error; // 抛出错误以触发重试机制
    }
  }

  /**
   * 提取章节内容（分批处理）
   */
  private async extractChaptersWithContent(
    crawler: QidianCrawlerV5,
    url: string,
    taskId: string,
    novelId: number,
  ): Promise<any[]> {
    // 首先获取章节列表（不含内容）
    const chapterList = await this.extractChapterList(crawler, url);

    if (!chapterList || chapterList.length === 0) {
      throw new Error('未找到章节列表');
    }

    // 更新总章节数
    await this.updateTaskStatus(taskId, {
      totalChapters: chapterList.length,
    });

    // 检查已处理的章节，实现断点续传
    const existingTask = await this.taskRepository.findOne({
      where: { taskId },
    });

    let startFromIndex = 0;
    if (existingTask && existingTask.processedChapters > 0) {
      startFromIndex = Math.min(
        existingTask.processedChapters,
        chapterList.length,
      );
      this.logger.log(
        `检测到已处理进度，从第 ${startFromIndex + 1} 章开始继续处理`,
      );
    }

    this.logger.log(
      `开始批量获取章节内容，共 ${chapterList.length} 章，从第 ${startFromIndex + 1} 章开始`,
    );

    // 分批获取章节内容，每批10章
    const batchSize = 10;
    const allChapters: any[] = [];

    // 从已处理的索引开始
    for (let i = startFromIndex; i < chapterList.length; i += batchSize) {
      const batch = chapterList.slice(i, i + batchSize);
      const batchChapters = await this.processBatch(
        crawler,
        batch,
        i,
        taskId,
        novelId,
        chapterList.length,
      );
      allChapters.push(...batchChapters);

      // 更新进度
      const progress = Math.min(
        ((i + batch.length) / chapterList.length) * 100,
        95,
      );
      await this.updateTaskStatus(taskId, {
        processedChapters: i + batch.length,
        progress: Math.round(progress * 100) / 100,
      });

      // 批次间延迟，避免请求过于频繁
      if (i + batchSize < chapterList.length) {
        await PageUtils.delay(10000); // 增加到10秒延迟
      }
    }

    return allChapters;
  }

  /**
   * 处理单个批次的章节
   */
  private async processBatch(
    crawler: QidianCrawlerV5,
    batch: any[],
    startIndex: number,
    taskId: string,
    novelId: number,
    totalChapters: number,
  ): Promise<any[]> {
    const batchChapters: any[] = [];

    for (let i = 0; i < batch.length; i++) {
      const chapter = batch[i];
      try {
        // 获取章节内容
        const content = await crawler.getSingleChapterContent(chapter.url);

        batchChapters.push({
          ...chapter,
          content: content || '',
          wordCount: content ? content.length : 0,
        });

        this.logger.log(
          `章节内容获取成功: ${chapter.title}, 字数: ${content ? content.length : 0}`,
        );

        // 实时保存单个章节（增量保存）
        if (content && content.trim()) {
          await this.saveSingleChapter(novelId, {
            ...chapter,
            content: content || '',
            wordCount: content ? content.length : 0,
          });
          this.logger.debug(`章节内容已保存到数据库: ${chapter.title}`);

          // 更新任务进度（断点续传）
          const currentProgress = startIndex + i + 1;
          await this.updateTaskStatus(taskId, {
            processedChapters: currentProgress,
            successChapters: currentProgress,
            progress:
              Math.round((currentProgress / totalChapters) * 100 * 100) / 100,
          });
        }
      } catch (error) {
        this.logger.warn(
          `章节内容获取失败: ${chapter.title}, 错误: ${error.message}`,
        );

        // 即使失败也保存章节，只是内容为空
        batchChapters.push({
          ...chapter,
          content: '',
          wordCount: 0,
        });
      }

      // 章节间延迟
      if (i < batch.length - 1) {
        await PageUtils.delay(5000); // 增加到5秒延迟
      }
    }

    return batchChapters;
  }

  /**
   * 提取章节列表（不含内容）
   */
  private async extractChapterList(
    crawler: QidianCrawlerV5,
    url: string,
  ): Promise<any[]> {
    // 创建一个临时的页面来获取章节列表
    await crawler.initBrowser();
    const browser = crawler['browser'];
    if (!browser) {
      throw new Error('浏览器未初始化');
    }
    const page = await browser.newPage();

    try {
      // 设置页面
      await crawler['setupPage'](page);

      // 导航到页面并等待加载
      await crawler['navigateToPage'](page, url);

      // 检测反爬虫措施
      if (await PageUtils.detectAntiCrawler(page)) {
        this.logger.warn('检测到反爬虫措施，正在处理...');
        await PageUtils.handleAntiCrawler(page);
      }

      // 等待目录加载
      const catalogElement = await PageUtils.waitForSelector(
        page,
        '#allCatalog',
        10000,
      );
      if (!catalogElement) {
        this.logger.warn('未找到章节目录 #allCatalog');
        return [];
      }

      const chapters = await PageUtils.safeEvaluate(
        page,
        () => {
          // 内联文本处理工具函数 - 浏览器兼容版本
          const safeTextContent = (element: Element | null): string => {
            return element?.textContent?.trim() || '';
          };

          const safeAttribute = (
            element: Element | null,
            attr: string,
          ): string => {
            return element?.getAttribute(attr) || '';
          };

          const catalogEl = document.querySelector('#allCatalog');
          if (!catalogEl) return [];

          const chapterItems = catalogEl.querySelectorAll('.chapter-item');
          const chapters: any[] = [];

          chapterItems.forEach((item) => {
            const chapterNameElement = item.querySelector('.chapter-name');
            const chapterTitle = safeTextContent(chapterNameElement);
            const chapterUrl = safeAttribute(chapterNameElement, 'href');

            // 是否收费检测
            const isLocked = !!item.querySelector('.chapter-locked');
            const isFree = isLocked ? 0 : 1;

            if (chapterTitle && chapterUrl) {
              chapters.push({
                title: chapterTitle,
                isFree: isFree,
                url: chapterUrl,
                content: '', // 不获取内容
                index: chapters.length + 1,
                volumeName: '正文',
              });
            }
          });

          return chapters;
        },
        '提取章节列表失败',
      );

      if (!chapters || chapters.length === 0) {
        this.logger.warn('未找到任何章节');
        return [];
      }

      this.logger.log(`找到${chapters.length}个章节（不含内容）`);
      return chapters;
    } catch (error) {
      this.logger.error('提取章节列表失败:', error);
      throw error;
    } finally {
      await page.close();
    }
  }

  /**
   * 保存单个章节内容
   */
  private async saveSingleChapter(
    novelId: number,
    chapterData: any,
  ): Promise<void> {
    try {
      // 查找现有章节
      let chapter = await this.chapterRepository.findOne({
        where: {
          novelId: novelId,
          chapterNumber: chapterData.index,
        },
      });

      if (chapter) {
        // 更新章节内容
        chapter.content = chapterData.content;
        chapter.wordCount = chapterData.wordCount;
        chapter.isFree = chapterData.isFree;
        chapter.volumeName = chapterData.volumeName || '正文';
      } else {
        // 创建新章节
        chapter = this.chapterRepository.create({
          novelId: novelId,
          title: chapterData.title,
          content: chapterData.content,
          chapterNumber: chapterData.index,
          wordCount: chapterData.wordCount,
          chapterUrl: chapterData.url,
          isFree: chapterData.isFree,
          volumeName: chapterData.volumeName || '正文',
        });
      }

      await this.chapterRepository.save(chapter);
    } catch (error) {
      this.logger.error(`保存单个章节失败: ${chapterData.title}`, error);
      // 不抛出错误，允许继续处理其他章节
    }
  }

  /**
   * 批量保存章节内容
   */
  private async saveChapterContents(
    novelId: number,
    chapters: any[],
    taskId: string,
  ): Promise<void> {
    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();

    try {
      for (const chapterData of chapters) {
        // 查找现有章节
        let chapter = await queryRunner.manager.findOne(NovelChapter, {
          where: {
            novelId: novelId,
            chapterNumber: chapterData.index,
          },
        });

        if (chapter) {
          // 更新章节内容
          chapter.content = chapterData.content;
          chapter.wordCount = chapterData.wordCount;
          chapter.isFree = chapterData.isFree;
          chapter.volumeName = chapterData.volumeName || '正文';
        } else {
          // 创建新章节
          chapter = queryRunner.manager.create(NovelChapter, {
            novelId: novelId,
            title: chapterData.title,
            content: chapterData.content,
            chapterNumber: chapterData.index,
            wordCount: chapterData.wordCount,
            chapterUrl: chapterData.url,
            isFree: chapterData.isFree,
            volumeName: chapterData.volumeName || '正文',
          });
        }

        await queryRunner.manager.save(chapter);
      }

      await queryRunner.commitTransaction();
      this.logger.log(`成功保存 ${chapters.length} 个章节内容`);
    } catch (error) {
      await queryRunner.rollbackTransaction();
      this.logger.error('保存章节内容失败:', error);
      throw error;
    } finally {
      await queryRunner.release();
    }
  }

  /**
   * 更新任务状态
   */
  private async updateTaskStatus(
    taskId: string,
    updates: Partial<NovelParseTask>,
  ): Promise<void> {
    await this.taskRepository.update({ taskId }, updates);
  }
}
